/**
 * 
 */
package com.pavco.caribbeanvisit.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.maps.client.overlay.Overlay;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.uibinder.client.UiTemplate;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.pavco.caribbeanvisit.client.RpcService;
import com.pavco.caribbeanvisit.client.RpcServiceAsync;
import com.pavco.caribbeanvisit.client.CaribbeanvisitConstants;
import com.pavco.caribbeanvisit.client.event.ClearCheckboxesEvent;
import com.pavco.caribbeanvisit.client.event.DisplayCountryMarkersEvent;
import com.pavco.caribbeanvisit.client.event.ReceivedCountriesWithAttrEvent;
import com.pavco.caribbeanvisit.client.event.ReceivedSearchResultsEvent;
import com.pavco.caribbeanvisit.client.event.ReceivedTagsEvent;
import com.pavco.caribbeanvisit.client.event.ShowButtonClickEvent;
import com.pavco.caribbeanvisit.client.eventhandlers.ClearCheckboxesEventHandler;
import com.pavco.caribbeanvisit.client.eventhandlers.DisplayCountryMarkersEventHandler;
import com.pavco.caribbeanvisit.client.eventhandlers.ReceivedCountriesWithAttrEventHandler;
import com.pavco.caribbeanvisit.client.eventhandlers.ReceivedSearchResultsEventHandler;
import com.pavco.caribbeanvisit.client.eventhandlers.ReceivedTagsEventHandler;
import com.pavco.caribbeanvisit.client.eventhandlers.ShowButtonEventHandler;
import com.pavco.caribbeanvisit.shared.models.Attraction;
import com.pavco.caribbeanvisit.shared.models.Country;

/**
 * @author wikid
 *
 */
public class ShowOptionsWidget extends Composite {
  HandlerManager eventBus;
  
  private static ShowOptionsWidgetUiBinder uiBinder = GWT
      .create(ShowOptionsWidgetUiBinder.class);

  @UiTemplate("ShowOptionsWidget.ui.xml")
  interface ShowOptionsWidgetUiBinder extends
      UiBinder<Widget, ShowOptionsWidget> {
  }

  @UiField HorizontalPanel dispCountryMarkersPanel;
  @UiField CheckBox dispCountryMarkersCheckbox;
  @UiField HTML dispCountryMarkersHtml;
  @UiField HTML attr4CountriesTitle;
  @UiField HorizontalPanel checkboxesPanel;
  @UiField HorizontalPanel buttonPanel;
  @UiField VerticalPanel countriesPanel;
  @UiField VerticalPanel tagsPanel;
  @UiField CheckBox allCountryCheckBox;
  @UiField CheckBox allAttractionCheckBox;
  @UiField Button showButton;
  @UiField Button clearButton;
  final CaribbeanvisitConstants constants = 
      GWT.create(CaribbeanvisitConstants.class);

  private PavcoMap map;
  private Panel searchResultsPanel = null;
  
  /**
   * Value = countryName
   */
  private final HashMap<CheckBox, String> countryValueMap = 
      new HashMap<CheckBox, String> ();
  /**
   * Value = attractionId
   */
  private final HashMap<CheckBox, String> tagValueMap = 
      new HashMap<CheckBox, String> ();
  private final ArrayList <SearchResult> searchResults =
      new ArrayList <SearchResult> ();
  
  
  @UiFactory DisclosurePanel createPanel() {
    return new DisclosurePanel(constants.options());
  }
  
  /**
   * @param map Map to show results on
   * @param searchResultsPanel The panel to show results
   */
  public ShowOptionsWidget(PavcoMap map, Panel searchResultsPanel) {
    initWidget(uiBinder.createAndBindUi(this));
    
    //this.listenToDisplayCountryMarkersEvent();
    //this.listenToClearCheckBoxesEvent();
    //this.listenToShowButtonClickEvent();
    //this.listenToReceivedCountriesWithAttractionsEvent();
    //this.listenToReceivedTagsEvent();
    //this.listenToReceivedSearchResultsEvent();
    
    applyStyles();

    // Can access @UiField after calling createAndBindUi
    dispCountryMarkersHtml.setHTML(constants.displayCountryMarkers());
    attr4CountriesTitle.setHTML(constants.showAttractionsForCountries());
    showButton.setText("Show");
    clearButton.setText("Clear");
    searchResultsPanel.setWidth("100%");
    
    allCountryCheckBox.setText(constants.all());
    allAttractionCheckBox.setText(constants.all());
    dispCountryMarkersCheckbox.setValue(true);
    buttonPanel.setSize("200px", "100%");
    
    allCountryCheckBox.addClickHandler(allCheckBoxHandler);
    allAttractionCheckBox.addClickHandler(allCheckBoxHandler);
  }
  
  public Button getShowButton() {
    return showButton;
  }
  
  public Button getClearButton() {
    return clearButton;
  }
  
  public CheckBox getDispCountryMarkerCheckBox() {
    return this.dispCountryMarkersCheckbox;
  }
  
  public List<String> getSelectedCountries() {
    ArrayList<String> countries = new ArrayList<String>();
    for(int i = 0; i < countriesPanel.getWidgetCount(); ++i) {
      if (countriesPanel.getWidget(i) != allCountryCheckBox) {
        if (countriesPanel.getWidget(i) instanceof CheckBox) {
          CheckBox countryCheckBox = (CheckBox) countriesPanel.getWidget(i);
          if (countryCheckBox.getValue()) {
            countries.add(countryValueMap.get(countryCheckBox));
          }
        }
      }
    }
    return countries;
  }
  
  public List<String> getSelectedTags() {
    ArrayList<String> tags = new ArrayList<String>();
    for(int i = 0; i < tagsPanel.getWidgetCount(); ++i) {
      if (tagsPanel.getWidget(i) != allAttractionCheckBox) {
        if (tagsPanel.getWidget(i).getClass().equals(CheckBox.class)) {
          CheckBox attractionCheckBox = (CheckBox) tagsPanel.getWidget(i);
          if (attractionCheckBox.getValue()) {
            tags.add(tagValueMap.get(attractionCheckBox));
          }
        }
      }
    }
    return tags;
  }
  
  /*public void load() {
    genericService.getCountriesWithAttractionsWithTags(
        getCountrWithAttrWithTagsCallback);
    genericService.getAllTags(getAllTagsCallback);
    
    attr4CountriesTitle.setStylePrimaryName("header3");
  }*/
  
  private void clearCheckBoxes(CellPanel panel) {
    for (Iterator<Widget> iter = panel.iterator(); iter.hasNext();) {
      Widget w = iter.next();
      if (w instanceof CheckBox) {
        CheckBox c = (CheckBox)w;
        c.setValue(false);
      }
    }
  }
  
  private void applyStyles() {
    allCountryCheckBox.addStyleName("allCheckbox");
    allAttractionCheckBox.addStyleName("allCheckbox");
  }
  
  private void clearResults() {
    // Remove search results
    for (SearchResult searchResult : searchResults) {
      searchResultsPanel.remove(searchResult);
    }

    //TODO: Look into this
    //this.searchResultsPanel.remove(clearResultsWidget);

    // Remove from map too
    // Done like this because it is thread-safe.  Don't use for-each loop
    Object[] overlays = map.getOverlays().toArray();
    for (int i = (overlays.length - 1); i > 0; --i) {
      Overlay o = (Overlay)overlays[i];
      map.removeOverlay(o);
    }
  }
  
  public AsyncCallback<ArrayList<Country>> getCountrWithAttrWithTagsCallback = 
    new AsyncCallback<ArrayList<Country>> () {

  @Override
  public void onFailure(Throwable caught) {
    ReceivedCountriesWithAttrEvent rce = new ReceivedCountriesWithAttrEvent();
    rce.setError(caught);
    eventBus.fireEvent(rce);     
  }

  @Override
  public void onSuccess(ArrayList<Country> result) {
    ReceivedCountriesWithAttrEvent rce = new ReceivedCountriesWithAttrEvent();
    rce.setCountries(result);
    eventBus.fireEvent(rce);     
  }
};

  public AsyncCallback<ArrayList<String>> getAllTagsCallback = 
      new AsyncCallback<ArrayList<String>>() {
  
    @Override
    public void onFailure(Throwable caught) {
      ReceivedTagsEvent rte = new ReceivedTagsEvent();
      rte.setError(caught);
      eventBus.fireEvent(rte);
    }
  
    @Override
    public void onSuccess(ArrayList<String> result) {
      ReceivedTagsEvent rte = new ReceivedTagsEvent();
      rte.setTags(result);
      eventBus.fireEvent(rte);
    }
  };
  
  private final ClickHandler countryCheckBoxHandler = new ClickHandler() {
    public void onClick(ClickEvent event) {
      Widget sender = (Widget)event.getSource();
      if (!(sender instanceof CheckBox)) {
        return;
      }
      Set <CheckBox> checkBoxSet = countryValueMap.keySet();
      for (CheckBox checkBox : checkBoxSet) {
        if (checkBox.getValue() == false) {
          allCountryCheckBox.setValue(false);
          return;
        }
      }
      allCountryCheckBox.setValue(true);
    }
  };
  
  private final ClickHandler tagCheckBoxHandler = new ClickHandler() {
    public void onClick(ClickEvent event) {
      Widget sender = (Widget)event.getSource();
      if (!(sender instanceof CheckBox)) {
        return;
      }
      Set <CheckBox> checkBoxSet = tagValueMap.keySet();
      for (CheckBox checkBox : checkBoxSet) {
        if (checkBox.getValue() == false) {
          allAttractionCheckBox.setValue(false);
          return;
        }        
      }
      allAttractionCheckBox.setValue(true);
    }
  };
  
  private final ClickHandler allCheckBoxHandler = new ClickHandler() {
    public void onClick(ClickEvent event) {
      Widget sender = (Widget)event.getSource();
      if (sender == allCountryCheckBox) {
        for(int i = 0; i < countriesPanel.getWidgetCount(); ++i) {
          if (countriesPanel.getWidget(i) != allCountryCheckBox) {
            if (countriesPanel.getWidget(i) instanceof CheckBox) {
              CheckBox countryCheckBox = (CheckBox) countriesPanel.getWidget(i);
              countryCheckBox.setValue(allCountryCheckBox.getValue());
            }
          }
        }
      } else if (sender == allAttractionCheckBox) {
        for(int i = 0; i < tagsPanel.getWidgetCount(); ++i) {
          if (tagsPanel.getWidget(i) != allAttractionCheckBox) {
            if (tagsPanel.getWidget(i).getClass().equals(CheckBox.class)) {
              CheckBox attractionCheckBox = (CheckBox) tagsPanel.getWidget(i);
              attractionCheckBox.setValue(allAttractionCheckBox.getValue());
            }
          }
        }
      }
    }
  };

  /**
   * Handler for all buttons, fires events
   */
  @UiHandler({"showButton", "clearButton"})
  public void doButtonClick(ClickEvent event) {
    Widget sender = (Widget) event.getSource();
    if (sender == showButton) {
      ArrayList<String> countryNames = new ArrayList<String>();
      ArrayList<String> tagNames = new ArrayList<String>();
      Set <CheckBox> checkBoxSet = countryValueMap.keySet();

      for (CheckBox checkBox : checkBoxSet) {
        countryNames.add(countryValueMap.get(checkBox));
      }
      checkBoxSet = tagValueMap.keySet();
      for (CheckBox checkBox : checkBoxSet) {
        tagNames.add(tagValueMap.get(checkBox));
      }
      ShowButtonClickEvent sbce = new ShowButtonClickEvent();
      sbce.setCountryNames(countryNames);
      sbce.setTagNames(tagNames);
      eventBus.fireEvent(sbce);
    } else if (sender == clearButton) {
      ClearCheckboxesEvent ccbEvent = new ClearCheckboxesEvent();
      eventBus.fireEvent(ccbEvent);
    }
  }

  /*private final ClickHandler clearResultsHandler  = new ClickHandler () {
    public void onClick(ClickEvent event) {
      clearResults();
    }
  };*/
  
  /**
   * This get's executed when show button is clicked
   */
  /*private void listenToShowButtonClickEvent() {
    eventBus.addHandler(ShowButtonClickEvent.TYPE,
        new ShowButtonEventHandler() {
      
      @Override
      public void onFailure(Throwable caught) {
        // TODO Auto-generated method stub
        
      }
      
      @Override
      public void onButtonClicked(ShowButtonClickEvent showButtonClickEvent) {        
        genericService.getAttractions(showButtonClickEvent.getCountryNames(), 
            showButtonClickEvent.getTagNames(), 
            new AsyncCallback<ArrayList<Attraction>> () {

          public void onFailure(Throwable caught) {
            // TODO Auto-generated method stub
          }

          public void onSuccess(ArrayList<Attraction> result) {
            ReceivedSearchResultsEvent event = new ReceivedSearchResultsEvent();
            event.setResults(result);
            eventBus.fireEvent(event);
          }
        });
      }
    });
  }*/
  
  /**
   * This get's executed when clear button is clicked
   */
  private void listenToClearCheckBoxesEvent() {
    eventBus.addHandler(ClearCheckboxesEvent.TYPE,
        new ClearCheckboxesEventHandler() {

          @Override
          public void onClearCheckboxes(ClearCheckboxesEvent event) {
            clearCheckBoxes(countriesPanel);
            clearCheckBoxes(tagsPanel);
          }
    });
  }

  private void listenToReceivedCountriesWithAttractionsEvent() {
    eventBus.addHandler(ReceivedCountriesWithAttrEvent.TYPE,
        new ReceivedCountriesWithAttrEventHandler() {
      
      @Override
      public void onFailure(Throwable caught) {
        // TODO Auto-generated method stub
        
      }
      
      @Override
      public void onReceived(ReceivedCountriesWithAttrEvent event) {
        if (event.isFailed()) {
          onFailure(event.getError());
          return;
        }
        
        ArrayList<Country> result = event.getCountries();
        if ((null == result) || (0 == result.size())) {
          return;
        }
        
        try {
          for (Country country : result) {
            final CheckBox checkBox = new CheckBox();
            checkBox.setText(country.getName());
            checkBox.addClickHandler(countryCheckBoxHandler);
            countriesPanel.add(checkBox);
            countryValueMap.put(checkBox, country.getName());
          }
        } catch (java.lang.NullPointerException npe) {
          System.out.println(npe.toString());
        }
      }
    });
  }
  
  private void listenToReceivedTagsEvent() {
    eventBus.addHandler(ReceivedTagsEvent.TYPE,
        new ReceivedTagsEventHandler() {

      @Override
      public void onFailure(Throwable caught) {
        // TODO Auto-generated method stub
        
      }
        
      @Override
      public void onReceived(ReceivedTagsEvent event) {
        ArrayList<String> result = event.getTags();
        if (null == result) {
          return;
        }
        
        for (String tag : result) {
          final CheckBox checkBox = new CheckBox();
          checkBox.setText(tag.substring(0, 1).toUpperCase() 
                            + tag.substring(1));
          checkBox.addClickHandler(tagCheckBoxHandler);
          tagsPanel.add(checkBox);
          tagValueMap.put(checkBox, tag);
        }
      }
    });
  }

  private void listenToReceivedSearchResultsEvent() {
    eventBus.addHandler(ReceivedSearchResultsEvent.TYPE,
        new ReceivedSearchResultsEventHandler() {

      @Override
      public void onFailed(Throwable caught) {
        // TODO Auto-generated method stub
      }
      
      @Override
      public void onReceived(ReceivedSearchResultsEvent event) {
        ArrayList<Attraction> result = event.getResults();
        
        if (event.isFailed()) {
          onFailed(event.getError());
        }
        
        if ((null == result) || (0 == result.size())) {
          return;
        }
        clearResults();
        for (Attraction attraction : result) {
          final SearchResult sr = new SearchResult(attraction);
          sr.addTo(map);
          searchResultsPanel.add(sr);
          searchResults.add(sr);
        }
      }
    });
  }
}